//go:build windows

package disklib

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/StackExchange/wmi"
	"github.com/urfave/cli/v2"
	"net/http"
	"os"
	"strings"
)

var (
	BuildTime string
	Version   = "1.0.0"
)

func Do() {
	app := &cli.App{
		Name:    "disk",
		Version: fmt.Sprintf("%s build:%s", Version, BuildTime),
		UsageText: "eg: disk idp -type=logic -filter=true -free=0.15 -c=0.3 \n " +
			"-type 可选值 physics 和 logic，分别对应物理磁盘和逻辑磁盘，默认为：logic \n" +
			"-filter=true 为可选值，默认为true，标记是否需要过滤无效节点(没有大小的磁盘或者没有序列号的逻辑卷标) \n" +
			"-free 可选参数,当-type=logic时只返回哪些空闲空间小于阈值的分区 \n" +
			"-c 可选参数,当-type=logic时可以为活动分区(系统启动盘)单独指定一个空闲比率，小于阈值才返回 \n" +
			"\n",
		Description: "进行各种磁盘操作和磁盘信息查询",
	}
	app.Commands = []*cli.Command{
		idpCommand(),
		extendCommand(),
	}

	if err := app.Run(os.Args); err != nil {
		fmt.Printf("命令行调用错误：%s ，请查看帮助文档", err.Error())
	}
}

func idpCommand() (comm *cli.Command) {
	comm = &cli.Command{
		Name:        "idp",
		UsageText:   "",
		Description: "适配idp的信息采集",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:        "type", // logic ,physics
				Value:       "logic",
				DefaultText: "logic",
			},
			&cli.BoolFlag{
				Name:        "filter", // 是否需要过滤无效节点(没有大小的磁盘或者没有序列号的逻辑卷标)，
				Value:       true,
				DefaultText: "true",
			},
			&cli.Float64Flag{
				Name:    "free",
				Aliases: []string{"yz", "fz"},
			},
			&cli.Float64Flag{
				Name:    "active",
				Aliases: []string{"c"},
			},
		},
		Action: func(ctx *cli.Context) error {
			var (
				t         string
				filter    bool
				threshold float64
				existThre bool
				cFree     float64
				err       error
			)
			t = ctx.String("type")
			filter = ctx.Bool("filter")
			existThre = ctx.IsSet("free")
			cFree = ctx.Float64("c")
			if existThre {
				threshold = ctx.Float64("free")
			}
			switch t {
			case "logic":
				err = execLogic(filter, existThre, threshold, cFree)
				if err != nil {
					return cli.Exit(fmt.Sprintf("获取逻辑磁盘信息时发送错误:%s", err.Error()), 1)
				}
			case "physics":
				err = execPhysics(filter)
				if err != nil {
					return cli.Exit(fmt.Sprintf("获取物理磁盘信息时发送错误:%s", err.Error()), 1)
				}
			default:
				return cli.Exit(fmt.Sprintf("type 参数:%s不正确", t), 1)
			}
			return nil
		},
	}
	return
}

func extendCommand() (comm *cli.Command) {
	comm = &cli.Command{
		Name:        "ext",
		UsageText:   "",
		Description: "扩展信息，采集的数据会直接发送给指定的地址",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:     "url", // 数据给谁
				Required: true,  // 必须参数
			},
			&cli.StringFlag{
				Name:        "type", // logic ,physics
				Value:       "logic",
				DefaultText: "logic",
			},
			&cli.BoolFlag{
				Name:        "filter", // 是否需要过滤无效节点(没有大小的磁盘或者没有序列号的逻辑卷标)，
				Value:       true,
				DefaultText: "true",
			},
		},
		Action: func(ctx *cli.Context) error {
			var (
				uri     string
				t       string
				filter  bool
				err     error
				logics  []Logic
				physics []Physics
				bs      []byte
			)
			uri = ctx.String(uri)
			t = ctx.String("type")
			filter = ctx.Bool("filter")
			switch t {
			case "logic":
				logics, err = getLogicInfos(filter)
				if err != nil {
					return cli.Exit(fmt.Sprintf("获取逻辑磁盘信息时发送错误:%s", err.Error()), 1)
				}
				bs, err = json.Marshal(logics)
			case "physics":
				physics, err = getPhysicsInfos(filter)
				if err != nil {
					return cli.Exit(fmt.Sprintf("获取物理磁盘信息时发送错误:%s", err.Error()), 1)
				}
				bs, err = json.Marshal(physics)
			default:
				return cli.Exit(fmt.Sprintf("type 参数:%s不正确", t), 1)
			}
			_, err = http.Post(uri, "application/json", bytes.NewReader(bs))
			if err != nil {
				return cli.Exit(fmt.Sprintf("发送数据时发送错误:%s", err.Error()), 1)
			}
			return nil
		},
	}
	return

}

func execPhysics(filter bool) (err error) {
	var ds []Physics
	ds, err = getPhysicsInfos(filter)
	if err != nil {
		return
	}
	bs, _ := json.Marshal(ds)
	w := bufio.NewWriter(os.Stdout)
	_, _ = w.WriteString(string(bs) + "\n")
	_ = w.Flush()
	return
}

func execLogic(filter bool, existFree bool, free, cFree float64) (err error) {
	var (
		ds          []Logic
		sysDeviceId string
	)
	ds, err = getLogicInfos(filter)
	if err != nil {
		return
	}
	if cFree != 0 {
		sysDeviceId, err = getActivePart()
		if err != nil {
			return
		}
	}

	var rs []Logic
	if existFree {
		for i := range ds {
			if ds[i].DeviceID == sysDeviceId {
				if float64(ds[i].FreeSpace)/float64(ds[i].Size) < cFree {
					rs = append(rs, ds[i])
				}
			} else {
				if float64(ds[i].FreeSpace)/float64(ds[i].Size) < free {
					rs = append(rs, ds[i])
				}
			}
		}
	} else {
		rs = append(rs, ds...)
	}
	var bs []byte
	if len(rs) > 0 {
		bs, _ = json.Marshal(rs)
	}
	w := bufio.NewWriter(os.Stdout)
	_, _ = w.WriteString(string(bs) + "\n")
	_ = w.Flush()
	return
}

type Logic struct {
	Caption            string `json:"caption"`            // eg:C: ,标题，也就是盘符
	DeviceID           string `json:"deviceID"`           // eg:C: , 在系统中的唯一标识符
	DriveType          uint32 `json:"driveType"`          // eg:3 , 磁盘类型，0-未知；1-No Root Directory；2-移动磁盘；3-本地磁盘；4-网络驱动器；5-光盘；6-RAM盘
	FileSystem         string `json:"fileSystem"`         // eg:NTFS , 磁盘格式
	FreeSpace          uint64 `json:"freeSpace"`          // eg:88191721472,空闲空间
	Size               uint64 `json:"size"`               // eg:255369482240 ,总空间
	SupportsDiskQuotas bool   `json:"supportsDiskQuotas"` // 是否支持磁盘配额
	VolumeName         string `json:"volumeName"`         // eg: 软件，逻辑卷别名
	VolumeSerialNumber string `json:"volumeSerialNumber"` // eg:2AF1A3B8 ,逻辑卷序列号，并非磁盘序列号。
}

func getLogicInfos(filtration bool) (rs []Logic, err error) {
	var data []Logic
	err = wmi.Query("SELECT * FROM Win32_LogicalDisk", &data)
	if err != nil {
		return nil, err
	}
	for index := range data {
		if filtration {
			if data[index].VolumeSerialNumber != "" {
				rs = append(rs, data[index])
			}
		}

	}
	return rs, nil
}

type Physics struct {
	Caption          string // eg:PCIe SSD ,对象的序列号
	DeviceID         string // eg: \\.\PHYSICALDRIVE1 ,磁盘驱动器与系统中的其他设备的唯一标识符
	FirmwareRevision string // eg:E8FM11.6 ,修订制造商分配的磁盘驱动器固件。
	InterfaceType    string // eg:SCSI ,物理磁盘驱动器的类型 （IDE、sata）
	Manufacturer     string // eg:(标准磁盘驱动器) ,制造商名称
	MediaType        string // eg:Fixed hard disk media ,由该设备使用或访问的媒体类型。
	Model            string // eg；PCIe SSD ，磁盘驱动器的制造商的型号。
	Name             string // eg:\\.\PHYSICALDRIVE1 ,名字
	SerialNumber     string // eg:6479_A721_8068_6489. 由制造商分配的号来识别物理介质。
	Size             uint64 // eg:256052966400 ,磁盘大小 单位字节 / 1024*1024*1024 才能得到G，
}

func getPhysicsInfos(filtration bool) (rs []Physics, err error) {
	var data []Physics
	err = wmi.Query("SELECT * FROM Win32_DiskDrive", &data)
	if err != nil {
		return nil, err
	}
	for index := range data {
		data[index].SerialNumber = strings.TrimSpace(data[index].SerialNumber)
		if filtration {
			if data[index].Size != 0 {
				rs = append(rs, data[index])
			}
		}
	}
	return rs, nil
}

// System 分区信息
type System struct {
	Primary     bool   `json:"primary"`
	SystemDrive string `json:"systemDrive"`
}

func getActivePart() (part string, err error) {
	var systems []System
	if err = wmi.Query(" SELECT * FROM Win32_OperatingSystem ", &systems); err != nil {
		return
	}
	for _, item := range systems {
		if item.Primary {
			return item.SystemDrive, nil
		}
	}
	err = errors.New("没有发现活动分区")
	return
}
